const Fs = @This();
const std = @import("std");
const fs = std.fs;
const mem = std.mem;
const SourceLocation: type = @import("SourceLocation.zig");
const ResStat = @import("root").ResStat;
const Iterator = @import("root").Iterator;

const c = @cImport({
    @cInclude("stdlib.h");
    @cInclude("unistd.h");
});

pub usingnamespace fs;

var option_init: *const u1 = undefined;

pub fn init(allocator: std.mem.Allocator) !void { // <><>
    option_init = try allocator.create(u1); // <+><+>
    errdefer allocator.destroy(option_init);

    const self_exe_dir_path: []const u8 = try fs.selfExeDirPathAlloc(allocator); // <><>
    defer allocator.free(self_exe_dir_path);

    try self_use_dir_path.init(allocator, self_exe_dir_path); // <+><+>
    errdefer self_use_dir_path.deinit(allocator);
    try cache_file.init(allocator); // <+><+>
    errdefer cache_file.deinit(allocator);

    return;
}
pub fn deinit(allocator: std.mem.Allocator) void {
    defer allocator.destroy(option_init);
    defer self_use_dir_path.deinit(allocator);
    defer cache_file.deinit(allocator);
}

const self_use_dir_path = struct {
    pub var data: [:0]const u8 = undefined;
    pub var cache: [:0]const u8 = undefined;

    var option_init: *const u1 = undefined;
    pub fn init(allocator: std.mem.Allocator, parent_path: []const u8) !void { // <><>
        var is_err: bool = false;
        self_use_dir_path.option_init = try allocator.create(u1); // <+><+>
        errdefer allocator.destroy(self_use_dir_path.option_init);
        const decls = comptime std.meta.declarations(self_use_dir_path);

        var res_s = try ResStat([:0]const u8, std.mem.Allocator, struct { // <><>
            pub fn func(
                slice: [:0]const u8,
                allocator1: std.mem.Allocator,
            ) void {
                allocator1.free(slice);
            }
        }.func).init(allocator); // <><>
        defer res_s.deinit(if (is_err) .{ .err = allocator } else .{ .normal = {} });
        errdefer is_err = true;
        inline for (decls) |decl| { // <+><+>
            if (std.meta.hasFn(self_use_dir_path, decl.name)) continue;

            const p_field: *[:0]const u8 = &@field(self_use_dir_path, decl.name);
            p_field.* = try std.fmt.allocPrintZ(allocator, "{s}/{s}", .{ parent_path, decl.name }); // <+><+>
            try res_s.reinit(p_field.*); // !!errdefer

            fs.makeDirAbsolute(@field(self_use_dir_path, decl.name)) catch |err| switch (err) {
                error.PathAlreadyExists => {},
                else => return err,
            }; // <+><+> but needn't deinit
        }
    }
    pub fn deinit(allocator: std.mem.Allocator) void {
        defer allocator.destroy(self_use_dir_path.option_init);
        defer inline for (comptime std.meta.declarations(self_use_dir_path)) |decl| {
            if (std.meta.hasFn(@This(), decl.name)) continue;
            defer allocator.free(@field(@This(), decl.name));
        };
    }
};

pub const data_file = struct {
    const DataFile: type = enum {
        @"user_data.db",
    };
    pub fn path(allocator: std.mem.Allocator, file: DataFile) ![:0]const u8 {
        return try std.fmt.allocPrintZ(allocator, "{s}/{s}", .{ self_use_dir_path.data, @tagName(file) });
    }
};

pub const cache_file: type = struct {
    var option_init: *const u1 = undefined;
    fn init(allocator: std.mem.Allocator) !void { // <><>
        @This().option_init = try allocator.create(u1);
        create_clear();
    }
    fn deinit(allocator: std.mem.Allocator) void {
        defer allocator.destroy(@This().option_init);
        return;
    }

    pub fn create_init(allocator: mem.Allocator) ![:0]u8 { // <><>
        const self_exe_path: []u8 = try fs.selfExeDirPathAlloc(allocator); // <><>
        defer allocator.free(self_exe_path);

        const filepath: [:0]u8 = try std.fmt.allocPrintZ(allocator, "{s}/temp_XXXXXX", .{self_use_dir_path.cache}); // <+><+>
        errdefer allocator.free(filepath);

        const fd: i32 = c.mkstemp(filepath); // <+><+> deinit_in(fs.deleteFileAbsolute) // <><>
        if (fd == -1) return error.failed;
        defer std.debug.assert(c.close(fd) != -1);
        errdefer fs.deleteFileAbsolute(filepath);
        return filepath;
    }
    pub fn create_deinit(allocator: mem.Allocator, rst: [:0]const u8) void {
        defer allocator.free(rst);
        defer fs.deleteFileAbsolute(rst) catch |err| switch (err) {
            // .FileNotFound => {}, // 暂时注释, 等到真正触发FileNotFound时再取消注释
            else => std.debug.print("err: {}\n", .{err}),
        };
        return;
    }
    pub fn create_clear() void {
        var dir: std.fs.Dir = fs.openDirAbsolute(self_use_dir_path.cache, .{ .iterate = true }) catch |err| {
            std.debug.print("src: {}, err: {}\n", .{ SourceLocation.init(@src()), err });
            @panic("nb");
        }; // <><>
        defer dir.close();
        var iter = dir.iterate();
        while (iter.next() catch |err| {
            std.debug.print("src: {}, err: {}\n", .{ SourceLocation.init(@src()), err });
            @panic("nb");
        }) |item| {
            if (item.kind == .file) {
                dir.deleteFile(item.name) catch |err| {
                    std.debug.print("src: {}, err: {}\n", .{ SourceLocation.init(@src()), err });
                    @panic("nb");
                };
            } else {
                std.debug.print("{}\nname:{s}\n", .{ SourceLocation.init(@src()), item.name });
            }
        }
    }
};
